package org.obsidianrad.server.services.user.ldap;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;

/**
 * 
 * @author Eligio Colutta
 * @version $Rev: 4 $
 * @lastautorcommit $Author: eliosh $
 * @lastdatecommit $Date: 2009-10-31 12:37:28 +0100 (sab, 31 ott 2009) $
 */
public class ObsidianLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {

	private static final Log logger = LogFactory.getLog(ObsidianLdapAuthoritiesPopulator.class);

	private SpringSecurityLdapTemplate ldapTemplate;

	private String rolePrefix = "ROLE_";

	private GroupToRoleLoader groupToRoleLoader;

	public ObsidianLdapAuthoritiesPopulator(ContextSource contextSource) {
		ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
	}

	protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
		Set<GrantedAuthority> auth = new HashSet<GrantedAuthority>();
		auth.add(new GrantedAuthorityImpl("ROLE_AUTHENTICATED"));
		return auth;
	}

	/**
	 * Obtains the authorities for the user who's directory entry is represented
	 * by the supplied LdapUserDetails object.
	 * 
	 * @param user
	 *            the user who's authorities are required
	 * @return the set of roles granted to the user.
	 */
	@Override
	public final GrantedAuthority[] getGrantedAuthorities(DirContextOperations user, String username) {
		String userDn = user.getNameInNamespace();

		if (logger.isDebugEnabled()) {
			logger.debug("Getting authorities for user " + userDn);
		}

		String accountEnabled = user.getStringAttribute("zimbraAccountStatus");

		if (!accountEnabled.equals("active")) {
			return new GrantedAuthority[0];
		}

		Set<GrantedAuthority> roles = getGroupMembershipRoles(user, username);

		Set<GrantedAuthority> extraRoles = getAdditionalRoles(user, username);

		if (extraRoles != null) {
			roles.addAll(extraRoles);
		}

		if (groupToRoleLoader != null) {
			Set<GrantedAuthority> loadRoles = groupToRoleLoader.loadRoles(roles);
			if (loadRoles != null) {
				roles.addAll(loadRoles);
			}
		}

		return (GrantedAuthority[]) roles.toArray(new GrantedAuthority[roles.size()]);
	}

	@SuppressWarnings("unchecked")
	private Set<GrantedAuthority> getGroupMembershipRoles(DirContextOperations user, String username) {
		Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
		String gid = user.getStringAttribute("gidNumber");

		Set userRoles = ldapTemplate.searchForSingleAttributeValues("ou=groups", "gidNumber={0}", new String[] { gid }, "displayName");

		Iterator it = userRoles.iterator();

		while (it.hasNext()) {
			String role = (String) it.next();

			role = role.toUpperCase();

			authorities.add(new GrantedAuthorityImpl(rolePrefix + role));
		}

		return authorities;
	}

	public GroupToRoleLoader getGroupToRoleLoader() {
		return groupToRoleLoader;
	}

	public void setGroupToRoleLoader(GroupToRoleLoader groupToRoleLoader) {
		this.groupToRoleLoader = groupToRoleLoader;
	}

}
